package org.skyui.views;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.skyui.accesscontrol.BeanAccessController;
import org.skyui.accesscontrol.UserOwnedBean;
import org.skyui.beanmanagers.AbstractBeanManager;
import org.skyui.beanmanagers.BeanRelationManager;
import org.skyui.beanmanagers.BeanSetManager;
import org.skyui.beans.AbstractBean;
import org.skyui.config.BeansTableViewConfig;
import org.skyui.controllers.AbstractBeansTableController;
import org.skyui.core.ControlledBeanItemContainer;

import com.vaadin.data.util.BeanItem;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Button.ClickEvent;

public abstract class AbstractBeansTableView<B extends AbstractBean> extends
	Panel implements UIView, Button.ClickListener,
	ItemClickEvent.ItemClickListener {

    public enum Action {
	ADD, COMMAND, REMOVE
    }

    public final static Action[] DEFAULT_ACTIONS = new Action[] { Action.ADD,
	    Action.REMOVE };

    /**
	 * 
	 */
    private static final long serialVersionUID = 4541954811815693692L;

    private Class<B> beanClass;

    private AbstractBeanManager beanManager;

    private Action[] buttonActions;

    private ControlledBeanItemContainer container;

    private AbstractBeansTableController controller;

    private boolean displayButtons = true;

    private String explanation;

    private HorizontalLayout horiz = new HorizontalLayout();

    private String name;

    private Map<String, Action> namesToActions = new LinkedHashMap<String, Action>();

    private List<Component> selectedItemTabs = new ArrayList<Component>(3);

    private Table table;

    private TabSheet ts;

    protected AbstractBeansTableView(String name, String explanation,
	    boolean showTableCaption, AbstractBeanManager beanManager,
	    boolean displayButtons) {

	this.name = name;
	this.explanation = explanation;

	this.setBeanManager(beanManager);
	this.displayButtons = displayButtons;

	String[] buttonNames;
	Action[] buttonActions;

	if (beanManager instanceof BeanRelationManager) {
	    buttonNames = ((BeanRelationManager) beanManager).getButtonNames();
	    buttonActions = ((BeanRelationManager) beanManager)
		    .getButtonActions();
	    if (showTableCaption)
		this.setCaption(((BeanRelationManager) beanManager)
			.getItemsCaption());
	} else {
	    buttonNames = BeansTableViewConfig
		    .getButtonNames((BeanSetManager) beanManager);
	    buttonActions = BeansTableViewConfig
		    .getButtonActions((BeanSetManager) beanManager);

	    if (showTableCaption)
		this.setCaption(BeansTableViewConfig
			.getItemsCaption((BeanSetManager) beanManager));
	}

	for (int i = 0; i < buttonNames.length; i++) {
	    this.namesToActions.put(buttonNames[i], buttonActions[i]);
	}
	this.beanClass = beanManager.getBeanClass();
	this.getLayout().setMargin(true);
	setLayout(this.getLayout());
	this.horiz.setMargin(false, false, true, false);
	refresh();
    }

    public void buttonClick(ClickEvent event) {
	this.getController().buttonClick(event);
    }

    public Action getAction(String name) {
	return this.namesToActions.get(name);
    }

    public AbstractBeanManager getBeanManager() {
	return beanManager;
    }

    public ControlledBeanItemContainer getContainer() {
	return this.container;
    }

    public AbstractBeansTableController getController() {
	return controller;
    }

    public String getExplanation() {
	return this.explanation;
    }

    public HorizontalLayout getHoriz() {
	return horiz;
    }

    public String getName() {
	return this.name;
    }

    public AbstractBean getSelectedBean() {
	if (this.table == null)
	    return null;
	Object itemId = this.table.getValue();
	if (itemId == null)
	    return null;
	return ((BeanItem<? extends AbstractBean>) this.table.getItem(itemId))
		.getBean();
    }

    public List<Component> getSelectedItemTabs() {
	return selectedItemTabs;
    }

    public TabSheet getTabSheet() {
	return ts;
    }

    public abstract void itemClick(ItemClickEvent event);

    public void refresh() {
	this.getLayout().removeAllComponents();
	this.getHoriz().removeAllComponents();
	removeSelectedComponent();
	this.setTabSheet(null);
	createComponents();
    }

    public void removeSelectedBean() {
	removeSelectedComponent();
	if (this.table == null)
	    return;
	Object itemId = this.table.getValue();
	if (itemId == null)
	    return;
	this.table.removeItem(itemId);
    }

    public void setBeanManager(AbstractBeanManager beanManager) {
	this.beanManager = beanManager;
    }

    public void setController(AbstractBeansTableController controller) {
	this.controller = controller;
    }

    public void setExplanation(String explanation) {
	this.explanation = explanation;
    }

    public void setHoriz(HorizontalLayout horiz) {
	this.horiz = horiz;
    }

    public void setName(String name) {
	this.name = name;
    }

    public void setSelectedItemTabs(List<Component> selectedItemTabs) {
	this.selectedItemTabs = selectedItemTabs;
    }

    public void setTabSheet(TabSheet ts) {
	this.ts = ts;
    }

    private void createComponents() {

	if (this.explanation != null) {
	    this.getLayout().addComponent(new Label(this.explanation));
	}

	this.table = createTable();

	this.getLayout().addComponent(this.table);
	this.getLayout().addComponent(this.getHoriz());

	if (this.displayButtons) {
	    for (String buttonName : this.namesToActions.keySet()) {
		Button button = new Button(buttonName);
		button.addListener(this);
		this.getHoriz().addComponent(button);
	    }
	}

	this.container = new ControlledBeanItemContainer(this.beanClass);

	Collection<B> beans;
	if (this.getBeanManager() instanceof BeanRelationManager) {
	    beans = ((BeanRelationManager) this.getBeanManager())
		    .getCachedBeansSet();
	} else {
	    beans = ((BeanSetManager) this.getBeanManager())
		    .getRefreshedBeans();
	}
	if (beans != null) {
	    for (B s : beans) {
		if (s instanceof UserOwnedBean) {
		    if (!BeanAccessController
			    .checkUserSessionAccessControl((UserOwnedBean) s))
			continue;
		}

		this.container.addBean(s);
	    }
	}

	this.table.setContainerDataSource(this.container);

	// this has to be done at the end
	try {
	    this.table.setVisibleColumns(this.beanClass.newInstance()
		    .getTableOrder().toArray());
	} catch (Exception e) {
	    e.printStackTrace();
	}

    }

    private Table createTable() {
	Table t = new Table("");
	t.setTableFieldFactory(new FieldFactoryImpl());
	t.setColumnCollapsingAllowed(true);
	t.setColumnReorderingAllowed(true);
	t.setSelectable(true);
	t.setMultiSelect(false);
	t.setRowHeaderMode(Table.ROW_HEADER_MODE_HIDDEN);
	t.setWidth("100%");
	t.setHeight("150px");
	t.addListener(this);
	return t;
    }

    protected void removeSelectedComponent() {
	for (Component selectedItemTab : this.getSelectedItemTabs()) {
	    selectedItemTab.detach();
	}
	if (this.getTabSheet() != null)
	    this.getTabSheet().removeAllComponents();
	this.getSelectedItemTabs().clear();
    }
}
